package com.ruoyi.aitcommon.aspectj;

import java.util.Collection;
import java.util.Date;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ruoyi.aitcommon.anotation.ServiceLog;
import com.ruoyi.aitcommon.domain.AitLog;
import com.ruoyi.aitcommon.exception.AITServiceException;
import com.ruoyi.aitcommon.service.IAitLogService;
import com.ruoyi.aitcommon.utils.AITConst;
import com.ruoyi.aitcommon.utils.AspectjUtils;
import com.ruoyi.aitcommon.utils.FlowUtils;
import com.ruoyi.aitcommon.utils.LogUtils;
import com.ruoyi.aitcommon.utils.RuntimeManager;
import com.ruoyi.aitcommon.vo.FlowContextVO;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;

import cn.hutool.core.util.StrUtil;

@Aspect
@Component
public class ServiceLogAspect {
	private static final Logger log = LogUtils.getLogger();
	@Autowired
	private IAitLogService logService;
	@Autowired
	private RuntimeManager runtimeManager;
	
	private Date startTime;
	private Date endTime;
	
	// 配置织入点
	@Pointcut("@annotation(com.ruoyi.aitcommon.anotation.ServiceLog)")
	public void pointCut() {
	}

	/**
	 * 切面处理逻辑
	 * 
	 * @param point 切入点
	 * @return
	 * @throws Throwable
	 */
	@Around("pointCut()")
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
		try {
//			方法执行前
//	      	获取参数
			Object[] args = AspectjUtils.getParmValues(point);

//			设置运行状态
			runtimeManager.setMehtod(AspectjUtils.getMehtodName(point));
			
//		    执行目标方法
			this.startTime=new Date();
			Object returnValue = point.proceed(args);
			this.endTime=new Date();
//		    方法执行后

//		    处理日志
			handleLog(point, null, returnValue);
//			返回
			return returnValue;
		} catch (Exception ex) {
//		    方法执行后
			this.endTime=new Date();
//		    处理日志
			handleLog(point, ex, null);
		} finally {

		}
		return null;
	}

	/**
	 * 处理完请求后执行
	 *
	 * @param joinPoint 切点
	 */
//	@AfterReturning(pointcut = "pointCut()", returning = "retobj")
//	public void doAfterReturning(JoinPoint joinPoint, Object retobj) {
//		handleLog(joinPoint, null, retobj);
//	}

	/**
	 * 拦截异常操作
	 * 
	 * @param joinPoint 切点
	 * @param ex        异常
	 */
//	@AfterThrowing(value = "pointCut()", throwing = "ex")
//	public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
//		handleLog(joinPoint, ex, null);
//	}

	/**
	 * 日志处理,包含异常的Log输出
	 * 
	 * @param joinPoint
	 * @param exp
	 * @param retobj
	 * @throws Exception 
	 */
	protected void handleLog(final JoinPoint joinPoint, final Exception exp, Object retobj) throws Exception {
		AitLog aitlog = new AitLog();
		try {
			// 获得注解
			ServiceLog svLog = AspectjUtils.getAnnotation(joinPoint, ServiceLog.class);
			if (svLog == null)
				return;
			// ========数据库日志=========//
			// 设置标题
			aitlog.setLogType("服务日志");
			aitlog.setApiName(svLog.value());
			aitlog.setStartDate(startTime);
			aitlog.setEndDate(endTime);
			Long run=this.endTime.getTime() - this.startTime.getTime();
			aitlog.setRunTime(run);
			// 设置方法名称
//			String className = joinPoint.getTarget().getClass().getName();
//			String methodName = joinPoint.getSignature().getName();
			aitlog.setApiMethod(AspectjUtils.getMehtodName(joinPoint));
//        	流程名称，流程ID
			FlowContextVO flowinfo = FlowUtils.getFlowContext();
			if (flowinfo != null) {
				aitlog.setFlowId(flowinfo.getFlowID());
				aitlog.setFlowName(flowinfo.getFlowName());
				aitlog.setFlowCode(flowinfo.getFlowCode());
			}
//            设置入口参数:args
			Object[] args = joinPoint.getArgs();
			StringBuilder inparm = new StringBuilder();
			String[] names = AspectjUtils.getParmNames(joinPoint);
			for (int i = 0; i < args.length; i++) {
				String argstr = "";
				if (args[i] != null) {
					argstr = AspectjUtils.getLogStr(args[i], AITConst.BIGSTRINGSIZE);
				}
				if (names[i] != null)
					inparm.append(names[i] + ":" + argstr + "\r\n");
				else if (args[i] != null)
					inparm.append("arg" + i + ":" + argstr + "\r\n");
			}
			aitlog.setInParam(StringUtils.substring(inparm.toString(), 0, AITConst.BIGSTRINGSIZE));
			setParFields(aitlog, args);

//            设置返回值
			Integer retCnt = 0;
			if (retobj != null) {
				aitlog.setRetParam(AspectjUtils.getLogStr(retobj, AITConst.BIGSTRINGSIZE));
				retCnt = getLogCnt(retobj);
			}
//            retcode,retmsg
			if (exp != null) {
				aitlog.setRetcode(String.valueOf(HttpStatus.ERROR));
				aitlog.setRetmsg(StrUtil.sub(exp.getMessage(), 0, AITConst.STRINGSIZE - 1));
			} else {
				aitlog.setRetcode(String.valueOf(HttpStatus.SUCCESS));
				if (retobj != null)
					aitlog.setRetmsg("成功");
			}

//			输出到文件Log
//			log.info("===========服务日志开始=============");
			LogUtils.info("服务日志[{}]-方法：{}", aitlog.getApiName(), aitlog.getApiMethod());
			LogUtils.info("服务日志-返回码/消息：{}/{}", aitlog.getRetcode(), aitlog.getRetmsg());
			LogUtils.info("服务日志-返回记录数：{}", retCnt);
			LogUtils.debug("服务日志-接收数据：{}", aitlog.getInParam());
			LogUtils.debug("服务日志-返回数据：{}", aitlog.getRetParam());
//			LogUtils.info("服务日志-结束:{}",aitlog.getApiName());

			FlowUtils.addBulletin("服务名：{}", aitlog.getApiName());
			FlowUtils.addBulletin("返回记录数：{}", retCnt);

//			抛出异常
			if (exp != null)
				throw exp;
		} catch (Exception ex) {
			// 记录本地异常日志
//			异常在服务切面中处理
			LogUtils.error("Service执行异常:[{}]{}", aitlog.getApiName(), aitlog.getApiMethod());
			log.error("异常堆栈:", ex);

			FlowUtils.addBulletin("服务异常:[{}]{}", aitlog.getApiName(), aitlog.getApiMethod());
			FlowUtils.addBulletin("异常信息:{}", ex.getMessage());
			FlowUtils.setRetCode(AITConst.ERRORCODE);
			
//			这里需要抛出异常,否则后续服务会继续执行
			throw new AITServiceException(ex);
		} finally {
			// 保存数据库
			// 减少日志数量,仅在报错时插入数据库日志
			if(!aitlog.getRetcode().equals(String.valueOf(HttpStatus.SUCCESS))){
				logService.insertAitLog(aitlog);
			}
		}
	}

//	获取返回值的记录数
	private Integer getLogCnt(Object var) {
		if (var instanceof Collection<?>) {
//			返回集合的个数
			return ((Collection<?>) var).size();
		} else if (var.getClass().isArray()) {
//			返回数组的个数
			return ((Object[]) var).length;
		} else if (var instanceof Integer) {
//			如果是sql update,直接返回整数
			return (Integer) var;
		} else
			return null;
	}

	// 设置json中的关键参数
	private void setParFields(AitLog aitlog, Object[] args) {
		if (args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				if (args[i] == null)
					continue;
				String substring = StringUtils.substring(args[i].toString(), 0, AITConst.STRINGSIZE);
				if (i == 0)
					aitlog.setParField0(substring);
				else if (i == 1)
					aitlog.setParField1(substring);
				else if (i == 2)
					aitlog.setParField2(substring);
				else if (i == 3)
					aitlog.setParField3(substring);
				else if (i == 4)
					aitlog.setParField4(substring);
				else if (i == 5)
					aitlog.setParField5(substring);
				else if (i == 6)
					aitlog.setParField6(substring);
				else if (i == 7)
					aitlog.setParField7(substring);
				else if (i == 8)
					aitlog.setParField8(substring);
				else if (i == 9)
					aitlog.setParField9(substring);
			}
		}
	}
}
